package com.qf.mybatisExam01.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

/**
 * 1.如果在web环境下使用该工具类，最好可以让每个每个线程在调用sqlsession方法是能够得到属于线程自己的同一个sqlsession对象
 * 2.避免重复创建对象
 *
 * 该工具类对外提供哪些方法
 *
 * 该工具类提供一个方法，用于获取指定类型的Dao层的接口的实现类的对象
 * T getMapper(Class<T> clazz)
 */
public class MybatisUtils {
    //类级别的成员变量 sqlSessionFactory 好处，全局唯一
    private static SqlSessionFactory sqlSessionFactory;
    //创建threadLocal对象，该对象保存线程私有的数据
    private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<SqlSession>();

    /**
     * 通过静态代码块初始化类级别的成员变量
     * 静态代码块仅在jvm加载class时执行一次
     */
    static{
        try {
            InputStream is =  Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder sfb = new SqlSessionFactoryBuilder();
            sqlSessionFactory = sfb.build(is);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 获取SqlSession对象
     * @return
     */
    public static SqlSession getSqlSession(){
        //先检查当前线程是否曾经获取过sqlSession对象，如果已经获取过，那么直接使用
        SqlSession sqlSession = threadLocal.get();
        if(sqlSession!=null){
            return sqlSession;
        }
        //如果当前线程没有获取过SqlSession对象，那么就创建一个新的sqlsession对象，并且保存起来
        sqlSession = sqlSessionFactory.openSession(); //该方法每次调用会新创建一个sqlsession对象
        threadLocal.set(sqlSession); //把新创建的sqlsession对象保存到threadLocal对象中
        return sqlSession;
    }

    /**
     * 提交事务
     */
    public static void commit(){
        //获取当前线程的sqlsession
        SqlSession sqlSession = getSqlSession();
        sqlSession.commit();
        //关闭当前线程的sqlsession对象
        closeSqlSession();
    }

    /**
     * 关闭sqlsession
     */
    private static void closeSqlSession() {
        //获取当前线程的sqlsession
        SqlSession sqlSession = getSqlSession();
        // 清理掉threadLocal对象中曾经保存过的sqlsession对象
        threadLocal.remove();
        //关闭
        sqlSession.close();
    }

    /**
     * 回滚事务
     */
    public static void rollback(){
        //获取当前线程的sqlsession
        SqlSession sqlSession = getSqlSession();

        sqlSession.rollback();

        //关闭当前线程的sqlsession对象
        closeSqlSession();
    }

    /**
     * 返回指定类型的dao曾接口的实现类对象
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getMapper(Class<T> clazz){
        SqlSession sqlSession = getSqlSession();
        return sqlSession.getMapper(clazz);
    }

}
